package mysql.cs;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.buffer.UnpooledHeapByteBuf;
import mysql.MysqlPacket;
import mysql.MysqlPacketHeader;
import mysql.cs.command.BinlogDumpCommand;
import mysql.cs.command.InitDBCommand;
import mysql.cs.command.QueryCommand;
import mysql.cs.command.QuitCommand;
import mysql.cs.handshake.HandshakeResponse41;
import mysql.cs.handshake.HandshakeV10;
import mysql.cs.packet.EOFPacket;
import mysql.cs.packet.ErrorPacket;
import mysql.cs.packet.OKPacket;
import mysql.cs.prop.CurrentOp;
import org.apache.commons.lang.ArrayUtils;

import java.io.IOException;

public class ComAndPackManager extends MysqlPacket {
    public final static byte SEQ1=(byte)0x01;
    public final static byte SEQ0=(byte)0x00;
    private HandshakeV10 handshakeV10;
    private HandshakeResponse41 handshakeResponse41;
    private String masterUser;
    private String masterPassword;
    private CurrentOp currentOp=CurrentOp.NONE;
    private ByteBuf pool=Unpooled.buffer(1024*1024*1024);


    private int currLength;
    private byte currSeq;
    public ComAndPackManager(String user,String password){
        this.handshakeV10=new HandshakeV10();
        this.handshakeResponse41=new HandshakeResponse41();
        masterPassword=password;
        masterUser=user;
        handshakeResponse41.setUsername(user);
        handshakeResponse41.setPassword(password);
    }
    public ComAndPackManager(){
        this.handshakeV10=new HandshakeV10();
        this.handshakeResponse41=new HandshakeResponse41();
    }
    public ComAndPackManager setUserAndPassword(String user,String password){
        masterPassword=password;
        masterUser=user;
        handshakeResponse41.setUsername(user);
        handshakeResponse41.setPassword(password);
        return this;
    }
    public OKPacket OKPacketEncode(byte[] data) throws IOException {
        OKPacket okPacket=new OKPacket();
        okPacket.endcode(data);
        return okPacket;
    }
    public EOFPacket EOFPacketEncode(byte[] data) throws IOException {
        EOFPacket eofPacket=new EOFPacket();
        eofPacket.encode(data);
        return eofPacket;
    }
    public static ErrorPacket ErrorPacketEncode(byte[] data) throws IOException {
        ErrorPacket errorPacket=new ErrorPacket();
        errorPacket.encode(data);
        return errorPacket;
    }
    public  ComAndPackManager HandshakeV10EncodeAndSetRES(byte[] data){
        this.handshakeV10.encode(data);
        handshakeResponse41.setCharsetNumber(handshakeV10.serverCharsetNumber);
        handshakeResponse41.setScrumbleBuff(ArrayUtils.addAll(handshakeV10.authPluginData1,handshakeV10.authPluginData2));
        return this;
    }

    public ByteBuf getPacket(byte[] payload,byte seq){
        mysqlPacketHeader = new MysqlPacketHeader();
        mysqlPacketHeader.setPayloadLength(payload.length);
        mysqlPacketHeader.setSequenceId(seq);
        ByteBuf out= Unpooled.directBuffer(payload.length+MysqlPacket.PACKETHEADERLEN);
        out.writeBytes(mysqlPacketHeader.decode()).writeBytes(payload);
        return out;
    }
    public ByteBuf getPacketNext(byte[] payload){
        mysqlPacketHeader = new MysqlPacketHeader();
        mysqlPacketHeader.setPayloadLength(payload.length);
        mysqlPacketHeader.setSequenceId(SEQ1);
        ByteBuf out= Unpooled.directBuffer(payload.length+MysqlPacket.PACKETHEADERLEN);
        out.writeBytes(mysqlPacketHeader.decode()).writeBytes(payload);
        return out;
    }

    public static byte[] getPacketNextHeader(byte[] payload){
        MysqlPacketHeader mysqlPacketHeader = new MysqlPacketHeader();
        mysqlPacketHeader.setPayloadLength(payload.length);
        mysqlPacketHeader.setSequenceId(SEQ1);
        return mysqlPacketHeader.decode();
    }


    public ByteBuf getPacketNew(byte[] payload){
        mysqlPacketHeader = new MysqlPacketHeader();
        mysqlPacketHeader.setPayloadLength(payload.length);
        mysqlPacketHeader.setSequenceId(SEQ0);
        ByteBuf out= Unpooled.directBuffer(payload.length+MysqlPacket.PACKETHEADERLEN);
        out.writeBytes(mysqlPacketHeader.decode()).writeBytes(payload);
        return out;
    }
    public ByteBuf handshakeResponse41RES() throws IOException {
        return getPacketNext(handshakeResponse41.decode());
    }
    public ByteBuf queryCommandPacket(String queryString) throws IOException {
        QueryCommand queryCommand=new QueryCommand();
        queryCommand.setQueryString(queryString);
        return getPacketNew(queryCommand.decode());
    }
    public static byte[] queryCommandPacketByte(String queryString) throws IOException {
        QueryCommand queryCommand=new QueryCommand();
        queryCommand.setQueryString(queryString);
        return queryCommand.decode();
    }
    public ByteBuf binlogDumpCommandPacket(String binlogFileName,long serverId,long binlogPosition) throws IOException {
        BinlogDumpCommand binlogDumpCommand=new BinlogDumpCommand();
        binlogDumpCommand.setBinlogFileName(binlogFileName);
        binlogDumpCommand.setSlaveServerId(serverId);
        binlogDumpCommand.setBinlogPosition(binlogPosition);
        byte[] payload=binlogDumpCommand.decode();
        return getPacketNew(binlogDumpCommand.decode());
    }

    public static byte[] binlogDumpCommandPacketByte(String binlogFileName,long serverId,long binlogPosition) throws IOException {
        BinlogDumpCommand binlogDumpCommand=new BinlogDumpCommand();
        binlogDumpCommand.setBinlogFileName(binlogFileName);
        binlogDumpCommand.setSlaveServerId(serverId);
        binlogDumpCommand.setBinlogPosition(binlogPosition);
        byte[] payload=binlogDumpCommand.decode();
        return binlogDumpCommand.decode();
    }
    public ByteBuf binlogDumpCommandWithID1AndPos4Packet(String binlogFileName) throws IOException {
        BinlogDumpCommand binlogDumpCommand=new BinlogDumpCommand();
        binlogDumpCommand.setBinlogFileName(binlogFileName);
        binlogDumpCommand.setSlaveServerId(1);
        binlogDumpCommand.setBinlogPosition(0x04);
        byte[] payload=binlogDumpCommand.decode();
        return getPacketNew(binlogDumpCommand.decode());
    }

    public static  byte[] binlogDumpCommandWithID1AndPos4PacketByte(String binlogFileName) throws IOException {
        BinlogDumpCommand binlogDumpCommand=new BinlogDumpCommand();
        binlogDumpCommand.setBinlogFileName(binlogFileName);
        binlogDumpCommand.setSlaveServerId(1);
        binlogDumpCommand.setBinlogPosition(0x04);
        byte[] payload=binlogDumpCommand.decode();
        return binlogDumpCommand.decode();
    }

    public ByteBuf initDBCommandPacket(String database) throws IOException {
        InitDBCommand initDBCommand=new InitDBCommand();
        initDBCommand.setDataabase(database);
        return getPacketNew(initDBCommand.decode());
    }
    public ByteBuf quitCommandPacket() throws IOException {
        QuitCommand quitCommand=new QuitCommand();
        return getPacketNew(quitCommand.decode());
    }
    public static byte[] quitCommandPacketDecode() throws IOException {
        QuitCommand quitCommand=new QuitCommand();
        return quitCommand.decode();
    }
    public static ComAndPackManager newInstance(){
        return new ComAndPackManager();
    }
    public static ComAndPackManager newInstance(String user,String password){
        return new ComAndPackManager(user,password);
    }

    public int getCurrLength() {
        return currLength;
    }

    public void setCurrLength(int currLength) {
        this.currLength = currLength;
    }

    public byte getCurrSeq() {
        return currSeq;
    }

    public void setCurrSeq(byte currSeq) {
        this.currSeq = currSeq;
    }

    public CurrentOp getCurrentOp() {
        return currentOp;
    }

    public void setCurrentOp(CurrentOp currentOp) {
        this.currentOp = currentOp;
    }

    public ByteBuf getPool() {
        if(pool.writerIndex()>=512*1024*1024){
            pool.discardReadBytes();
        }
        return pool;
    }

    public HandshakeV10 getHandshakeV10() {
        return handshakeV10;
    }

    public void setHandshakeV10(HandshakeV10 handshakeV10) {
        this.handshakeV10 = handshakeV10;
    }

    public HandshakeResponse41 getHandshakeResponse41() {
        return handshakeResponse41;
    }

    public void setHandshakeResponse41(HandshakeResponse41 handshakeResponse41) {
        this.handshakeResponse41 = handshakeResponse41;
    }
}
